package com.samcai.swiperecyclerview.divider;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorRes;
import android.support.annotation.DimenRes;
import android.support.annotation.DrawableRes;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;


/**
 * Created by yqritc on 2015/01/08.
 */
public abstract class FlexibleDividerDecoration extends RecyclerView.ItemDecoration {

	private static final int DEFAULT_SIZE = 2;

	private static final int[] ATTRS = new int[] { android.R.attr.listDivider };

	protected enum DividerType {
		DRAWABLE, PAINT, COLOR
	}

	protected DividerType mDividerType = DividerType.DRAWABLE;

	protected VisibilityProvider mVisibilityProvider;

	protected PaintProvider mPaintProvider;

	protected ColorProvider mColorProvider;

	protected DrawableProvider mDrawableProvider;

	protected SizeProvider mSizeProvider;

	protected boolean mShowLastDivider;

	private Paint mPaint;

	protected FlexibleDividerDecoration(Builder<?> builder) {

		if (builder.mPaintProvider != null) {
			mDividerType = DividerType.PAINT;
			mPaintProvider = builder.mPaintProvider;
		} else if (builder.mColorProvider != null) {
			mDividerType = DividerType.COLOR;
			mColorProvider = builder.mColorProvider;
			mPaint = new Paint();
			setSizeProvider(builder);
		} else {
			mDividerType = DividerType.DRAWABLE;
			if (builder.mDrawableProvider == null) {
				TypedArray a = builder.mContext.obtainStyledAttributes(ATTRS);
				final Drawable divider = a.getDrawable(0);
				a.recycle();
				mDrawableProvider = new DrawableProvider() {

					@Override
					public Drawable drawableProvider(int position, RecyclerView parent) {

						return divider;
					}
				};
			} else {
				mDrawableProvider = builder.mDrawableProvider;
			}
			mSizeProvider = builder.mSizeProvider;
		}

		mVisibilityProvider = builder.mVisibilityProvider;
		mShowLastDivider = builder.mShowLastDivider;
	}

	private void setSizeProvider(Builder<?> builder) {

		mSizeProvider = builder.mSizeProvider;
		if (mSizeProvider == null) {
			mSizeProvider = new SizeProvider() {

				@Override
				public int dividerSize(int position, RecyclerView parent) {

					return DEFAULT_SIZE;
				}
			};
		}
	}

	@Override
	public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {

		int lastChildPosition = -1;
		int childCount = mShowLastDivider ? parent.getChildCount() : parent.getChildCount() - 1;
		for (int i = 0; i < childCount; i++) {
			View child = parent.getChildAt(i);
			// int childPosition = parent.getChildAdapterPosition(child);
			int childPosition = parent.getChildPosition(child);
			if (childPosition < lastChildPosition) {
				// Avoid remaining divider when animation starts
				continue;
			}
			lastChildPosition = childPosition;

			if (ViewCompat.getAlpha(child) < 1) {
				// Avoid remaining divider when animation starts
				continue;
			}

			if (mVisibilityProvider.shouldHideDivider(childPosition, parent)) {
				continue;
			}

			Rect bounds = getDividerBound(childPosition, parent, child);
			switch (mDividerType) {
			case DRAWABLE:
				Drawable drawable = mDrawableProvider.drawableProvider(childPosition, parent);
				drawable.setBounds(bounds);
				drawable.draw(c);
				break;
			case PAINT:
				mPaint = mPaintProvider.dividerPaint(childPosition, parent);
				c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
				break;
			case COLOR:
				mPaint.setColor(mColorProvider.dividerColor(childPosition, parent));
				mPaint.setStrokeWidth(mSizeProvider.dividerSize(childPosition, parent));
				c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
				break;
			}
		}
	}

	@Override
	public void getItemOffsets(Rect rect, View v, RecyclerView parent, RecyclerView.State state) {

		// int position = parent.getChildAdapterPosition(v);
		int position = parent.getChildPosition(v);
		setItemOffsets(rect, position, parent);
	}

	protected abstract Rect getDividerBound(int position, RecyclerView parent, View child);

	protected abstract void setItemOffsets(Rect outRect, int position, RecyclerView parent);

	/**
	 * Interface for controlling divider visibility
	 */
	public interface VisibilityProvider {

		/**
		 * Returns true if divider should be hidden.
		 *
		 * @param position
		 *            Divider position
		 * @param parent
		 *            RecyclerView
		 * @return True if the divider at position should be hidden
		 */
		boolean shouldHideDivider(int position, RecyclerView parent);
	}

	/**
	 * Interface for controlling paint instance for divider drawing
	 */
	public interface PaintProvider {

		/**
		 * Returns {@link android.graphics.Paint} for divider
		 *
		 * @param position
		 *            Divider position
		 * @param parent
		 *            RecyclerView
		 * @return Paint instance
		 */
		Paint dividerPaint(int position, RecyclerView parent);
	}

	/**
	 * Interface for controlling divider color
	 */
	public interface ColorProvider {

		/**
		 * Returns {@link android.graphics.Color} value of divider
		 *
		 * @param position
		 *            Divider position
		 * @param parent
		 *            RecyclerView
		 * @return Color value
		 */
		int dividerColor(int position, RecyclerView parent);
	}

	/**
	 * Interface for controlling drawable object for divider drawing
	 */
	public interface DrawableProvider {

		/**
		 * Returns drawable instance for divider
		 *
		 * @param position
		 *            Divider position
		 * @param parent
		 *            RecyclerView
		 * @return Drawable instance
		 */
		Drawable drawableProvider(int position, RecyclerView parent);
	}

	/**
	 * Interface for controlling divider size
	 */
	public interface SizeProvider {

		/**
		 * Returns size value of divider. Height for horizontal divider, width
		 * for vertical divider
		 *
		 * @param position
		 *            Divider position
		 * @param parent
		 *            RecyclerView
		 * @return Size of divider
		 */
		int dividerSize(int position, RecyclerView parent);
	}

	public static class Builder<T extends Builder<?>> {

		private Context mContext;

		protected Resources mResources;

		private PaintProvider mPaintProvider;

		private ColorProvider mColorProvider;

		private DrawableProvider mDrawableProvider;

		private SizeProvider mSizeProvider;

		private VisibilityProvider mVisibilityProvider = new VisibilityProvider() {

			@Override
			public boolean shouldHideDivider(int position, RecyclerView parent) {

				return false;
			}
		};

		private boolean mShowLastDivider = false;

		public Builder(Context context) {

			mContext = context;
			mResources = context.getResources();
		}

		public T paint(final Paint paint) {

			return paintProvider(new PaintProvider() {

				@Override
				public Paint dividerPaint(int position, RecyclerView parent) {

					return paint;
				}
			});
		}

		@SuppressWarnings("unchecked")
		public T paintProvider(PaintProvider provider) {

			mPaintProvider = provider;
			return (T) this;
		}

		public T color(final int color) {

			return colorProvider(new ColorProvider() {

				@Override
				public int dividerColor(int position, RecyclerView parent) {

					return color;
				}
			});
		}

		@SuppressWarnings("deprecation")
		public T colorResId(@ColorRes int colorId) {

			return color(mResources.getColor(colorId));
		}

		@SuppressWarnings("unchecked")
		public T colorProvider(ColorProvider provider) {

			mColorProvider = provider;
			return (T) this;
		}

		@SuppressWarnings("deprecation")
		public T drawable(@DrawableRes int id) {

			return drawable(mResources.getDrawable(id));
		}

		public T drawable(final Drawable drawable) {

			return drawableProvider(new DrawableProvider() {

				@Override
				public Drawable drawableProvider(int position, RecyclerView parent) {

					return drawable;
				}
			});
		}

		@SuppressWarnings("unchecked")
		public T drawableProvider(DrawableProvider provider) {

			mDrawableProvider = provider;
			return (T) this;
		}

		public T size(final int size) {

			return sizeProvider(new SizeProvider() {

				@Override
				public int dividerSize(int position, RecyclerView parent) {

					return size;
				}
			});
		}

		public T sizeResId(@DimenRes int sizeId) {

			return size(mResources.getDimensionPixelSize(sizeId));
		}

		@SuppressWarnings("unchecked")
		public T sizeProvider(SizeProvider provider) {

			mSizeProvider = provider;
			return (T) this;
		}

		@SuppressWarnings("unchecked")
		public T visibilityProvider(VisibilityProvider provider) {

			mVisibilityProvider = provider;
			return (T) this;
		}

		@SuppressWarnings("unchecked")
		public T showLastDivider() {

			mShowLastDivider = true;
			return (T) this;
		}

		protected void checkBuilderParams() {

			if (mPaintProvider != null) {
				if (mColorProvider != null) {
					throw new IllegalArgumentException(
							"Use setColor method of Paint class to specify line color. Do not provider ColorProvider if you set PaintProvider.");
				}
				if (mSizeProvider != null) {
					throw new IllegalArgumentException(
							"Use setStrokeWidth method of Paint class to specify line size. Do not provider SizeProvider if you set PaintProvider.");
				}
			}
		}
	}
}
